import sys
import os
from threading import Lock

# 将项目根目录添加到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import yaml
import serial
from serial.tools import list_ports
import time
import threading
from typing import Dict, List
from utils.Logger import logger
from utils.process_data import DataProcessor
import traceback



class SerialPortManager:
    def __init__(self, config_path: str):
        """初始化串口管理器
        Args:
            config_path (str): 配置文件路径
        """
        # 配置日志
        
        self.logger = logger

        self.data_callback = None
        
        # 加载配置文件
        self.config = self._load_config(config_path)
        
        # 存储串口对象
        self.serial_ports: Dict[str, serial.Serial] = {}
        
        # 存储线程对象
        self.threads: Dict[str, threading.Thread] = {}
        
        # 运行标志
        self.running = True
        
        # 初始化所有串口
        self._init_serial_ports()

        self.data_processor = DataProcessor()
        self.parsed_data = self.data_processor.data

        self.data_lock = Lock()  # 添加锁

    def _load_config(self, config_path: str) -> dict:
        """加载配置文件
        Args:
            config_path (str): 配置文件路径
        Returns:
            dict: 配置信息
        """
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                return yaml.safe_load(f)
        except Exception as e:
            self.logger.error(f"加载配置文件失败: {e}")
            raise

    def _init_serial_ports(self):
        """初始化所有串口"""
        # 获取系统中所有可用的串口
        available_ports = list_ports.comports()
        if self.config['produce'] == 0:
            port_configs = self.config['serial_ports_test']
        else:
            port_configs = self.config['serial_ports']
        for port_config in port_configs:
            try:
                description = port_config['description']
                # 根据描述查找对应的串口
                matched_port = None
                for port in available_ports:
                    if description.lower() in port.description.lower():
                        matched_port = port.device
                        break
                
                if matched_port is None:
                    self.logger.error(f"未找到描述为 '{description}' 的串口")
                    continue
                    
                # 使用找到的串口号初始化
                ser = serial.Serial(
                    port=matched_port,  # 使用匹配到的串口号
                    baudrate=port_config['baudrate'],
                    bytesize=port_config['bytesize'],
                    parity=port_config['parity'],
                    stopbits=port_config['stopbits'],
                    timeout=port_config['timeout']
                )
                self.serial_ports[matched_port] = ser
                self.logger.info(f"串口 {matched_port} ({description}) 初始化成功")
                
                # 为每个串口创建一个数据发送线程
                thread = threading.Thread(
                    target=self._port_send_loop,
                    args=(matched_port, port_config['commands']),
                    daemon=True
                )
                self.threads[matched_port] = thread
                
            except Exception as e:
                self.logger.error(f"串口 {description} 初始化失败: {e}\n{traceback.format_exc()}")

    def _calculate_crc(self, data: bytes) -> int:
        """计算 Modbus CRC16
        Args:
            data: 需要计算校验和的数据
        Returns:
            int: 16位CRC校验值
        """
        crc = 0xFFFF
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x0001:
                    crc = (crc >> 1) ^ 0xA001
                else:
                    crc >>= 1
        return crc

    def _verify_modbus_response(self, response: bytes) -> bool:
        """验证 Modbus 响应的 CRC
        Args:
            response: 完整的 Modbus 响应数据
        Returns:
            bool: CRC校验是否通过
        """
        if len(response) < 3:  # 至少需要1字节数据+2字节CRC
            return False
        
        # 获取接收到的CRC（最后两个字节，低字节在前）
        received_crc = (response[-1] << 8) | response[-2]
        # 计算除CRC外的数据的CRC
        calculated_crc = self._calculate_crc(response[:-2])
        
        return received_crc == calculated_crc
    def get_expected_length(self, command_str):
        """计算预期的响应长度"""
        try:
            cmd_bytes = bytes.fromhex(command_str)
            if len(cmd_bytes) < 6:  # Modbus 命令最少6字节
                return None
                
            function_code = cmd_bytes[1]  # 功能码
            if function_code == 0x03:  # 读保持寄存器
                register_count = (cmd_bytes[4] << 8) + cmd_bytes[5]  # 寄存器数量
                return 5 + register_count * 2  # 响应: 地址(1) + 功能码(1) + 字节数(1) + 数据(N*2) + CRC(2)
            elif function_code == 0x06:  # 写单个寄存器
                return 8  # 响应固定8字节
            elif function_code == 0x10:  # 写多个寄存器
                return 8  # 响应固定8字节
            return None
        except Exception as e:
            self.logger.error(f"计算预期长度失败: {str(e)}")
            return None

    def _port_send_loop(self, port_name: str, commands):
        """串口数据发送循环"""
        ser = self.serial_ports[port_name]
        
        while self.running:
            try:
                if ser.is_open:
                    # 处理命令列表
                    if isinstance(commands, list):
                        # 连续发送所有命令
                        for cmd_config in commands:
                            # 获取命令信息
                            command_str = cmd_config['cmd']
                            description = cmd_config.get('description', '')
                            
                            # 发送命令
                            command_bytes = bytes.fromhex(command_str.replace(" ", ""))
                            ser.write(command_bytes)
                            self.logger.debug(f"串口 {port_name} 发送数据: {command_str} ({description})")
                            expected_length = self.get_expected_length(command_str)
                            if expected_length is None:
                                self.logger.error(f"无法确定预期响应长度: {command_str}")
                            
                            time.sleep(0.1)
                                # 等待并读取响应
                            max_wait = 1.0  # 最大等待时间1秒
                            start_time = time.time()
                            response = b''
                            if ser.in_waiting:
                                while (time.time() - start_time) < max_wait:
                                    if ser.in_waiting:
                                        response += ser.read(ser.in_waiting)
                                        # 检查是否收到足够的数据
                                        if len(response) >= expected_length:
                                            break
                                    time.sleep(0.1)
                                
                                # 检查响应长度
                                if len(response) < expected_length:
                                    self.logger.warning(
                                        f"响应数据不完整: 预期{expected_length}字节, 实际接收{len(response)}字节"
                                    )
                                
                                if response:
                                    response_hex = ' '.join([f'{b:02X}' for b in response])
                                    self.logger.debug(f"串口 {port_name} 接收数据: {response_hex}")
                                    
                                    # response = ser.read(ser.in_waiting)
                                if len(response) > 0:
                                    # 验证CRC
                                    if self._verify_modbus_response(response):
                                        data_hex = response.hex(' ').upper()
                                        self.parsed_data = self.data_processor._parse_response(
                                            port_name, 
                                            command_str, 
                                            data_hex,
                                            cmd_config.get('description', '')  # 传入命令描述
                                        )
                                        logger.info({"pressure": self.parsed_data["2F"]["Second"]['更衣室']["压差"]})
                                        # logger.info(f"解析后的数据: {self.parsed_data}")
                                        # if self.data_callback and self.parsed_data:
                                        #     logger.info(f"调用回调函数success")
                                        #     self.data_callback(port_name, self.parsed_data)
                                    else:
                                        self.logger.warning(f"串口 {port_name} CRC校验失败: {response.hex(' ').upper()}")
                    
                        # 所有命令发送完成后，等待间隔时间
                        interval = commands[0].get('interval', 1000)  # 使用第一个命令的间隔时间
                        time.sleep(interval / 1000.0 - len(commands)/10)
                    
                else:
                    time.sleep(1)  # 串口未打开时等待
                    
            except Exception as e:
                self.logger.error(f"串口 {port_name} 发送数据失败: {traceback.format_exc()}")
                time.sleep(1)  # 发生错误时等待1秒

    def start(self):
        """启动所有串口的数据发送线程"""
        for port_name, thread in self.threads.items():
            thread.start()
            self.logger.info(f"串口 {port_name} 数据发送线程已启动")

    def stop(self):
        """停止所有串口的数据发送"""
        self.running = False
        # 等待所有线程结束
        for thread in self.threads.values():
            thread.join()
        # 关闭所有串口
        for ser in self.serial_ports.values():
            if ser.is_open:
                ser.close()
        self.logger.info("所有串口已关闭")

    def set_data_callback(self, callback):
        """设置数据回调函数"""
        self.data_callback = callback

    def process_data(self, port_name, command_str, data_hex, cmd_config):
        try:
            with self.data_lock:  # 使用锁保护数据处理过程
                self.parsed_data = self.data_processor._parse_response(
                    port_name, 
                    command_str, 
                    data_hex,
                    cmd_config.get('description', '')
                )
                
                if self.data_callback and self.parsed_data:
                    self.data_callback(port_name, self.parsed_data)
                    
        except Exception as e:
            logger.error(f"数据处理失败: {e}")

def dev():
    # 创建串口管理器实例
    try:
        manager = SerialPortManager('config.yaml')
        manager.start()
        
        # 保持主程序运行
        try:
            while True:
                print(manager.parsed_data["2F"]["Second"]['更衣室']["压差"])
                time.sleep(3)
        except KeyboardInterrupt:
            print("\n正在停止服务...")
            manager.stop()
            
    except Exception as e:
        logger.error(f"程序运行错误: {traceback.format_exc()}")

if __name__ == "__main__":
    dev()